package com.zhx.common.chat.service.strategy.msg;

import cn.hutool.core.bean.BeanUtil;
import com.zhx.common.chat.dao.MessageDao;
import com.zhx.common.chat.domain.entity.Message;
import com.zhx.common.chat.domain.enums.MessageTypeEnum;
import com.zhx.common.chat.domain.vo.request.ChatMessageReq;
import com.zhx.common.chat.service.adapter.MessageAdapter;
import com.zhx.common.common.util.AssertUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.lang.reflect.ParameterizedType;

/**
 * Description: 消息处理器抽象类
 */
public abstract class AbstractMsgHandler<Req> {
    @Resource
    private MessageDao messageDao;

    //Req -> 子类的消息类型
    private Class<Req> bodyClass;

    /**
     * 不用在子类中一个个写了，子类中也会执行这个。
     * 该方法在类的构造完成后执行，用于初始化类的一些基本属性和注册当前实例。
     * 该方法没有参数，也没有返回值。
     * 主要步骤包括：
     * 1. 获取当前类(子类）的泛型超类类型（AbstractMsgHandler），并从中提取出请求体的类类型。
     * 2. 将消息类型和当前实例注册到消息处理器工厂中。
     */
    @PostConstruct
    private void init() {
        // 获取当前类的泛型父类类型，并转换为ParameterizedType
        ParameterizedType genericSuperclass = (ParameterizedType) this.getClass().getGenericSuperclass();
        // 从泛型父类类型中获取实际的类型参数，即请求体的类型 -> TextMsgReq / ...
        this.bodyClass = (Class<Req>) genericSuperclass.getActualTypeArguments()[0];
        // 将当前实例（子类）根据其消息类型注册到消息处理器工厂
        MsgHandlerFactory.register(getMsgTypeEnum().getType(), this);
    }

    /**
     * 消息类型
     */
    abstract MessageTypeEnum getMsgTypeEnum();

    protected void checkMsg(Req body, Long roomId, Long uid) {}

    @Transactional
    public Long checkAndSaveMsg(ChatMessageReq request, Long uid) {
        Req body = this.toBean(request.getBody());
        //统一校验
        AssertUtil.allCheckValidateThrow(body);
        //子类扩展校验
        checkMsg(body, request.getRoomId(), uid);

        Message insert = MessageAdapter.buildMsgSave(request, uid);
        //统一保存,所有消息通用的
        messageDao.save(insert);
        //子类扩展保存，不同的消息不同的 -> Req body，填充Message.extra
        saveMsg(insert, body);
        return insert.getId();
    }

    /**
     * 将传入的对象转换为指定类型的Req对象。
     * 首先检查传入对象是否已经是Req类型，如果是，则直接返回；
     * 如果不是，则使用BeanUtil的转换功能将其转换为Req类型。
     *
     * @param body 待转换的对象。该对象可以是Req类型或其子类，或任意类型（此时会尝试使用BeanUtil进行转换）。
     * @return 转换后的Req对象。如果转换失败，可能会抛出异常。
     */
    private Req toBean(Object body) {
        // 检查传入的对象是否已经是Req类型或其子类
        if (bodyClass.isAssignableFrom(body.getClass())) {
            return (Req) body;
        }
        // 使用BeanUtil将对象转换为Req类型
        return BeanUtil.toBean(body, bodyClass);
    }


    protected abstract void saveMsg(Message message, Req body);

    /**
     * 展示消息
     */
    public abstract Object showMsg(Message msg);

    /**
     * 被回复时——展示的消息
     */
    public abstract Object showReplyMsg(Message msg);

    /**
     * 会话列表——展示的消息
     */
    public abstract String showContactMsg(Message msg);

}
